package cc.rengu.igas.route.core.realize.impl;

import cc.rengu.igas.route.common.constant.SmartRouteParamConstant;
import cc.rengu.igas.route.common.dao.MultiDyncDataStaticMapper;
import cc.rengu.igas.route.common.dao.MultiRatioWeightCfgMapper;
import cc.rengu.igas.route.common.dao.RuleCfgMapper;
import cc.rengu.igas.route.common.dao.impl.MultiDyncDataStaticMapperImpl;
import cc.rengu.igas.route.common.dao.impl.MultiRatioWeightCfgMapperImpl;
import cc.rengu.igas.route.common.dao.impl.RuleCfgMapperImpl;
import cc.rengu.igas.route.common.entity.MultiDyncDataStatic;
import cc.rengu.igas.route.common.entity.MultiRatioWeightCfg;
import cc.rengu.igas.route.common.entity.RuleCfg;
import cc.rengu.igas.route.common.enums.RouteRuleTypeEnum;
import cc.rengu.igas.route.common.util.IPv4Util;
import cc.rengu.igas.route.core.helper.RouterHelper;
import cc.rengu.igas.route.core.model.MultiDyncDataRuleGroup;
import cc.rengu.igas.route.core.realize.RouterDataInit;
import cc.rengu.jradp.mods.dbs.Database;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.dao.DstChannelInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.DstChannelInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.DstChannelInfo;
import cc.rengu.oltp.utility.util.DateUtil;
import cc.rengu.oltp.utility.util.StringUtil;
import cc.rengu.utility.cache.UnifiedCache;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 多路比例路由动态数据统计数据加载到系统缓存
 * 1. 如有无效的路由静态配置数据则统计数据删除
 *
 * @author xionglz
 * @version 1.0
 * @date 2020-03-23
 */
public class RouterDataInitMultiDyncData implements RouterDataInit {
    private RgLogger rglog = RgLog.getLogger(this.getClass().getName());
    private static ReentrantLock lock = new ReentrantLock();
    private static boolean update = false;

    static boolean isUpdate() {
        return update;
    }

    void waitload() {
        boolean islocked = false;
        try {
            islocked = lock.tryLock(3, TimeUnit.SECONDS);
        } catch (Exception e) {
            rglog.error(cc.rengu.utility.base.StringUtil.ExceptionToString(e));
        } finally {
            if (islocked) {
                lock.unlock();
            }
        }
    }

    @Override
    public int loadRouterData() throws Exception {
        lock.lock();
        try {
            update = true;

            rglog.info("多路比例路由统计数据加载开始");
            //开启游标查询所有智能路由规则配置表
            RuleCfgMapper ruleCfgMapper = new RuleCfgMapperImpl();
            List<RuleCfg> listRuleCfg = ruleCfgMapper.getAllRuleCfg();
            if (null == listRuleCfg || listRuleCfg.isEmpty()) {
                rglog.info("路由配置表无数据，无需加载！");
                return 0;
            }
            for (RuleCfg ruleCfg : listRuleCfg) {
                if (!RouteRuleTypeEnum.MULTI_RATE.getCode().equalsIgnoreCase(ruleCfg.getRuleType())) {
                    continue;
                }
                //落地动态数据
                ArrayList<MultiDyncDataStatic> multiDyncDataStatics = saveDyncDataMulti(ruleCfg);
                if (null == multiDyncDataStatics || multiDyncDataStatics.isEmpty()) {
                    continue;
                }
                //加载动态统计数据到内存
                createMemoryData(multiDyncDataStatics);
            }
            rglog.info("多路比例路由统计数据加载结束");
            return 0;
        } finally {
            lock.unlock();
            update = false;
        }
    }

    public int loadRouterDataByInst(String instId) throws Exception {
        if (StringUtil.isEmptyOrNull(instId) || AppParamConstant.DEFAULT_INSTID.equals(instId)) {
            rglog.error("必须上送机构,且机构不能为默认机构");
            return -1;
        }

        rglog.info("多路比例路由统计数据加载开始");
        //开启游标查询所有智能路由规则配置表
        RuleCfgMapper ruleCfgMapper = new RuleCfgMapperImpl();
        List<RuleCfg> listRuleCfg = ruleCfgMapper.getAllRuleCfg();
        if (null == listRuleCfg || listRuleCfg.isEmpty()) {
            rglog.info("路由配置表无数据，无需加载！");
            return 0;
        }
        for (RuleCfg ruleCfg : listRuleCfg) {
            if (!ruleCfg.getInstId().equals(instId)) {
                continue;
            }
            if (!RouteRuleTypeEnum.MULTI_RATE.getCode().equalsIgnoreCase(ruleCfg.getRuleType())) {
                continue;
            }
            //落地动态数据
            ArrayList<MultiDyncDataStatic> multiDyncDataStatics = saveDyncDataMulti(ruleCfg);
            if (null == multiDyncDataStatics || multiDyncDataStatics.isEmpty()) {
                continue;
            }
            //加载动态统计数据到内存
            createMemoryData(multiDyncDataStatics);
        }
        rglog.info("多路比例路由统计数据加载结束");
        return 0;
    }

    /**
     * 落地多路比例动态统计数据
     * 1. 若存在则使用现有数据，不存在则根据配置初始话数据落地到数据库
     *
     * @param ruleCfg 路由配置入口
     * @return 成功返回动态数据list，不需要加载返回null
     * @throws Exception
     */
    private ArrayList<MultiDyncDataStatic> saveDyncDataMulti(RuleCfg ruleCfg) throws Exception {
        rglog.info("机构<{}>规则id<{}>多路比例路由统计数据落地到数据库start", ruleCfg.getInstId(), ruleCfg.getRuleId());
        if (!RouterHelper.judgeCommonRecordStatus(ruleCfg.getRuleStatus(), ruleCfg.getAuditStatus())) {
            rglog.info("多路比例路由未启用！instId<{}>ruleId<{}>", ruleCfg.getInstId(), ruleCfg.getRuleId());
            return null;
        }
        //根据rule_id查询比例路由配置表
        MultiRatioWeightCfgMapper multiRatioWeightCfgMapper = new MultiRatioWeightCfgMapperImpl();
        List<MultiRatioWeightCfg> listMultiRatioWeightCfg = multiRatioWeightCfgMapper.getMultiRatioWeightCfg(ruleCfg.getInstId(), ruleCfg.getRuleId());
        if (null != listMultiRatioWeightCfg) {
            rglog.info("查询多路比例路由成功<{}><{}>", ruleCfg.getInstId(), ruleCfg.getRuleId());
            if (listMultiRatioWeightCfg.size() < SmartRouteParamConstant.INT_TWO) {
                rglog.error("多路比例路由<size:{}>,多路比例路由配置数据条数错误,<ruleId:{}>", listMultiRatioWeightCfg.size(), listMultiRatioWeightCfg.get(0).getRuleId());
                throw new Exception("多路比例路由配置数据条数错误");
            }
            int weightTotal = 0;
            ArrayList<MultiDyncDataStatic> multiDyncDataStatics = new ArrayList<>();
            for (MultiRatioWeightCfg multiRatioWeightCfg : listMultiRatioWeightCfg) {
                //查询通道信息表确认通道状态
                DstChannelInfoMapper dstChannelInfoMapper = new DstChannelInfoMapperImpl();
                DstChannelInfo dstChannelInfo = dstChannelInfoMapper.selectDstChannelInfoByPrimaryKey(multiRatioWeightCfg.getInstId(), multiRatioWeightCfg.getDstChannelId());
                if (null != dstChannelInfo) {
                    //获取配置状态，状态非正常的过滤
                    if (RouterHelper.judgeCommonRecordStatus(multiRatioWeightCfg.getRuleStatus(), multiRatioWeightCfg.getAuditStatus())) {
                        MultiDyncDataStatic multiDyncDataStatic = new MultiDyncDataStatic();
                        multiDyncDataStatic.setChannelId(multiRatioWeightCfg.getDstChannelId());
                        multiDyncDataStatic.setChannelRealStatus(dstChannelInfo.getDstChannelStatus());
                        multiDyncDataStatic.setChannelStatus(dstChannelInfo.getDstChannelStatus());
                        multiDyncDataStatic.setInstId(ruleCfg.getInstId());
                        multiDyncDataStatic.setMachineId(IPv4Util.ipv4Address);
                        multiDyncDataStatic.setRuleId(ruleCfg.getRuleId());
                        multiDyncDataStatic.setUpdateTime(DateUtil.getCurrentDateTime(SmartRouteParamConstant.FORMAT_DATE_TIME23));
                        multiDyncDataStatic.setWeight(multiRatioWeightCfg.getWeight());
                        multiDyncDataStatic.setWeightUsed(0);
                        rglog.info("多路比例路由机构<{}>规则id<{}>通道<{}>", ruleCfg.getInstId(), ruleCfg.getRuleId(), multiDyncDataStatic.getChannelId());
                        multiDyncDataStatics.add(multiDyncDataStatic);
                    } else {
                        rglog.info("多路比例路由机构<{}>规则id<{}>通道<{}>状态异常不加载", ruleCfg.getInstId(), ruleCfg.getRuleId(), multiRatioWeightCfg.getDstChannelId());
                    }
                } else {
                    rglog.error("多路比例路由配置数据错误,无对应通道<{}><{}>", multiRatioWeightCfg.getInstId(), multiRatioWeightCfg.getDstChannelId());
                    throw new Exception("多路比例路由配置数据错误");
                }
                weightTotal = weightTotal + multiRatioWeightCfg.getWeight();
            }
            if (SmartRouteParamConstant.INT_ONE_HUNDRED != weightTotal) {
                rglog.error("多路比例路由<weightTotal:{}>,多路比例路由配置总权重不等100,<ruleId:{}>", weightTotal, listMultiRatioWeightCfg.get(0).getRuleId());
                throw new Exception("多路比例路由配置总权重不等100");
            }

            //落地到数据库
            save2Db(multiDyncDataStatics);

            if (multiDyncDataStatics.size() > 0) {
                rglog.info("机构<{}>规则id<{}>多路比例路由统计数据落地到数据库success", ruleCfg.getInstId(), ruleCfg.getRuleId());
                return multiDyncDataStatics;
            } else {
                rglog.debug("多路比例路由配置错误！配置了路由入口规则<{}>但是没有配置实际的多路比例路由数据", ruleCfg.getRuleId());
                return null;
            }
        } else {
            rglog.info("多路比例路由配置错误！配置了路由入口规则<{}>但是没有配置实际的多路比例路由数据", ruleCfg.getRuleId());
            throw new Exception("多路比例路由配置错误");
        }
    }

    private void save2Db(ArrayList<MultiDyncDataStatic> multiDyncDataStatics) throws Exception {
        MultiDyncDataStaticMapper multiDyncDataStaticMapper = new MultiDyncDataStaticMapperImpl();
        for (MultiDyncDataStatic multiDyncDataStatic : multiDyncDataStatics) {
            //查询动态统计数据表、若存在数据则判断通道状态，若通道不正常则更新统计数据中通道的状态
            MultiDyncDataStatic multiDyncDataStaticTmp = multiDyncDataStaticMapper.getMultiDyncDataStaticByPk(multiDyncDataStatic.getInstId(),
                    multiDyncDataStatic.getRuleId(), multiDyncDataStatic.getChannelId(), multiDyncDataStatic.getMachineId());
            if (null == multiDyncDataStaticTmp) {
                //插入数据库多路比例动态数据统计信息，若报主键重复则continue
                int iRetCode = multiDyncDataStaticMapper.insertMultiDyncDataStatic(multiDyncDataStatic);
                if (Database.DBS_PKEY_DUPLICATE == iRetCode) {
                    //防止集群启动时其他应用插入导致失败
                    rglog.info("重复插入过滤掉该条数据继续");
                } else if (Database.DBS_SUCCESS == iRetCode) {
                    rglog.info("初始化落地动态数据成功");
                } else {
                    rglog.info("初始化落地动态数据失败");
                    throw new Exception("初始化落地动态数据失败");
                }
            } else {
                if (!multiDyncDataStatic.getChannelStatus().equalsIgnoreCase(multiDyncDataStaticTmp.getChannelStatus())) {
                    //以通道信息表中通道状态覆盖动态统计表的通道状态
                    if (SmartRouteParamConstant.YES.equalsIgnoreCase(multiDyncDataStatic.getChannelStatus())) {
                        multiDyncDataStaticMapper.setMultiDyncDataValidByPk(multiDyncDataStatic.getInstId(),
                                multiDyncDataStatic.getRuleId(), multiDyncDataStatic.getChannelId(), multiDyncDataStatic.getMachineId());
                    } else {
                        multiDyncDataStaticMapper.setMultiDyncDataInvalidByPk(multiDyncDataStatic.getInstId(),
                                multiDyncDataStatic.getRuleId(), multiDyncDataStatic.getChannelId(), multiDyncDataStatic.getMachineId());
                    }
                }
            }
        }
    }

    private void createMemoryData(ArrayList<MultiDyncDataStatic> multiDyncDataStatics) throws Exception {
        multiDyncDataStatics.sort(Comparator.comparingInt(MultiDyncDataStatic::getWeight));
        MultiDyncDataRuleGroup dyncDataRuleGroup = new MultiDyncDataRuleGroup(multiDyncDataStatics);
        dyncDataRuleGroup.setNowAndFullIndex();
        //将动态数据统计表存入系统缓存
        String key = multiDyncDataStatics.get(0).getInstId() + ":" + multiDyncDataStatics.get(0).getRuleId();
        UnifiedCache.set(SmartRouteParamConstant.ROUTE_DATA_MULTI, key, dyncDataRuleGroup);
        rglog.info("多路比例路由数据存入系统缓存成功，key=<{}>", key);
    }
}
